Перейти к основному содержимому

Арифметика указателей

Что такое арифметика указателей

Арифметика указателей — это математические операции с адресами в памяти. Указатели можно увеличивать, уменьшать, складывать с числами и вычитать друг из друга.

Инкремент и декремент

Перемещение на один элемент

#include <stdio.h>

int main() {
int numbers[5] = {10, 20, 30, 40, 50};
int *ptr = numbers; // Указывает на numbers[0]

printf("Начальная позиция: %d (адрес: %p)\n", *ptr, ptr);

ptr++; // Переходим к следующему элементу
printf("После ptr++: %d (адрес: %p)\n", *ptr, ptr);

++ptr; // Еще один переход
printf("После ++ptr: %d (адрес: %p)\n", *ptr, ptr);

return 0;
}

Сложение и вычитание с числами

Перемещение на N позиций

#include <stdio.h>

int main() {
int data[8] = {1, 2, 3, 4, 5, 6, 7, 8};
int *ptr = data; // Указывает на data[0]

printf("ptr: %d\n", *ptr); // 1
printf("ptr + 3: %d\n", *(ptr + 3)); // 4
printf("ptr + 7: %d\n", *(ptr + 7)); // 8

// Изменяем позицию указателя
ptr += 5; // Теперь указывает на data[5]
printf("После ptr += 5: %d\n", *ptr); // 6

ptr -= 2; // Возвращаемся на 2 позиции назад
printf("После ptr -= 2: %d\n", *ptr); // 4

return 0;
}

Вычисления с указателями разных типов

#include <stdio.h>

int main() {
// Массивы разных типов
int integers[4] = {10, 20, 30, 40};
char characters[4] = {'A', 'B', 'C', 'D'};

int *intPtr = integers;
char *charPtr = characters;

printf("=== РАЗМЕРЫ ШАГОВ ===\n");
printf("int* стартовый адрес: %p\n", intPtr);
printf("int* + 1: %p (сдвиг на %zu байт)\n", intPtr + 1, sizeof(int));
printf("int* + 2: %p (сдвиг на %zu байт)\n", intPtr + 2, sizeof(int) * 2);

printf("\nchar* стартовый адрес: %p\n", charPtr);
printf("char* + 1: %p (сдвиг на %zu байт)\n", charPtr + 1, sizeof(char));
printf("char* + 2: %p (сдвиг на %zu байт)\n", charPtr + 2, sizeof(char) * 2);

return 0;
}

Разность указателей

Расстояние между элементами

#include <stdio.h>

int main() {
int sequence[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

int *start = &sequence[2]; // Указывает на элемент 2
int *end = &sequence[8]; // Указывает на элемент 8

int distance = end - start; // Количество элементов между ними

printf("Начальный элемент: %d (индекс 2)\n", *start);
printf("Конечный элемент: %d (индекс 8)\n", *end);
printf("Расстояние: %d элементов\n", distance); // 6

// Проверяем вычисление
printf("8 - 2 = %d (проверка)\n", 8 - 2);

return 0;
}

Практические применения

Обход массива указателем

#include <stdio.h>

int main() {
float prices[5] = {19.99, 34.50, 12.75, 45.20, 28.90};
float *ptr = prices;

printf("Цены товаров:\n");

for (int i = 0; i < 5; i++) {
printf("Товар %d: %.2f руб.\n", i + 1, *ptr);
ptr++; // Переходим к следующей цене
}

return 0;
}

Поиск элемента в массиве

#include <stdio.h>

int main() {
int products[6] = {101, 205, 308, 412, 506, 609};
int size = 6;
int target = 308;

int *start = products;
int *current = start;
int *end = start + size;

printf("Поиск товара с кодом: %d\n", target);

while (current < end) {
printf("Проверяем код: %d\n", *current);

if (*current == target) {
int position = current - start; // Вычисляем индекс
printf("✅ Товар найден на позиции %d\n", position);
break;
}

current++;
}

if (current == end) {
printf("❌ Товар не найден\n");
}

return 0;
}

Сравнение участков массива

#include <stdio.h>

int main() {
int sales[8] = {150, 200, 180, 220, 175, 300, 250, 190};

int *firstHalf = sales; // Первые 4 дня
int *secondHalf = sales + 4; // Последние 4 дня

int firstSum = 0, secondSum = 0;

// Суммируем первую половину
for (int i = 0; i < 4; i++) {
firstSum += *(firstHalf + i);
}

// Суммируем вторую половину
for (int i = 0; i < 4; i++) {
secondSum += *(secondHalf + i);
}

printf("Продажи за первые 4 дня: %d\n", firstSum);
printf("Продажи за последние 4 дня: %d\n", secondSum);

if (secondSum > firstSum) {
printf("📈 Рост продаж на %d\n", secondSum - firstSum);
} else {
printf("📉 Снижение продаж на %d\n", firstSum - secondSum);
}

return 0;
}

Границы и безопасность

Проверка выхода за границы

#include <stdio.h>

int main() {
int data[5] = {10, 20, 30, 40, 50};
int *start = data;
int *end = data + 5; // Указывает ЗА последний элемент
int *current = start;

printf("Безопасный обход массива:\n");

while (current < end) { // Проверяем границы
printf("Элемент: %d (позиция: %ld)\n", *current, current - start);
current++;
}

printf("Достигнут конец массива\n");

return 0;
}

Ошибки в арифметике указателей

Частые ошибки
// ❌ Выход за границы массива
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
ptr += 10; // Указывает далеко за пределы массива!
printf("%d", *ptr); // Непредсказуемое значение или сбой

// ❌ Вычитание указателей на разные массивы
int arr1[3] = {1, 2, 3};
int arr2[3] = {4, 5, 6};
int *ptr1 = arr1;
int *ptr2 = arr2;
int diff = ptr2 - ptr1; // Бессмысленная операция!

// ❌ Арифметика с нулевым указателем
int *nullPtr = NULL;
nullPtr++; // Неопределенное поведение!

// ❌ Сложение двух указателей
int *ptr1 = &arr1[0];
int *ptr2 = &arr1[2];
int *sum = ptr1 + ptr2; // Недопустимая операция!
Правила безопасности
  • Выполняйте арифметику только с указателями на элементы одного массива
  • Всегда проверяйте границы массива
  • Не используйте арифметику с NULL указателями
  • Вычитание указателей допустимо только для одного массива
  • Сложение двух указателей недопустимо

Арифметика указателей — мощный инструмент для эффективной навигации по памяти, но требует внимательности к границам данных.